package com.cctc.mychat.client;

import com.cctc.mychat.client.command.impl.LoginCommand;
import com.cctc.mychat.client.command.impl.manager.ManagerCommand;
import com.cctc.mychat.client.handler.ChatResponseMessageHandler;
import com.cctc.mychat.client.handler.LoginResponseMessageHandler;
import com.cctc.mychat.client.handler.LogoutResponseMessageHandler;
import com.cctc.mychat.client.handler.heartbeat.HeartBeatRequestTimer;
import com.cctc.mychat.common.handler.ReaderIdleStateHandler;
import com.cctc.mychat.common.protocol.codec.MessageCodec;
import com.cctc.mychat.common.protocol.codec.MessageDecoder;
import com.cctc.mychat.common.protocol.codec.MessageEncoder;
import com.cctc.mychat.common.protocol.codec.UnpacketDecoder;
import com.cctc.mychat.common.session.SessionManager;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.util.Date;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class MyChatClient {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 8000;
    private static final int MAX_RETRY_COUNT = 5;

    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        bootstrap
                .group(workerGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.SO_SNDBUF, 32 * 1024)
                .option(ChannelOption.SO_RCVBUF, 32 * 1024)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1800 * 1000)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel channel) throws Exception {
                        ChannelPipeline pipeline = channel.pipeline();
                        pipeline.addLast(new ReaderIdleStateHandler(1800))
                                .addLast(new UnpacketDecoder())
                                .addLast(new MessageCodec())
                                .addLast(new HeartBeatRequestTimer())
                                .addLast(new LoginResponseMessageHandler())
                                .addLast(new ChatResponseMessageHandler())
                                .addLast(new LogoutResponseMessageHandler());
                    }
                });
        connect(bootstrap, MAX_RETRY_COUNT);
    }

    private static void connect(Bootstrap bootstrap, int retryCount) {
        bootstrap.connect(HOST, PORT)
                .addListener(future -> {
                    if (future.isSuccess()) {
                        System.out.println("[MyChatClient] 连接成功，启动控制台线程... ...");
                        Channel channel = ((ChannelFuture) future).channel();
                        startCommandThread(channel);
                    } else if (retryCount == 0) {
                        System.err.println("[MyChatClient] 连接失败，重试次数已用完，放弃连接！！！");
                    } else {
                        // 第几次重连
                        int order = (MAX_RETRY_COUNT - retryCount) + 1;
                        // 本次重连间隔时间
                        int retrySleepDelay = 1 << order;
                        System.err.println("[MyChatClient] 连接失败，开始进行第 " + order + " 次重连... ...");
                        bootstrap.config().group().schedule(() -> connect(bootstrap, retryCount - 1), retrySleepDelay, TimeUnit.SECONDS);
                    }
                });
    }

    private static void startCommandThread(Channel channel) {
        LoginCommand loginCommand = new LoginCommand();
        ManagerCommand managerCommand = new ManagerCommand();
        Scanner scanner = new Scanner(System.in);
        new Thread(() -> {
            System.out.println(">>> 欢迎使用 [MyChat] 聊天系统 <<<");
            while (!Thread.interrupted()) {
                if (!SessionManager.hasLogin(channel)) {
                    System.out.println("[MyChat] 用户未登录，请先登录系统！！！");
                    loginCommand.exec(scanner, channel);
                } else {
                    System.out.println("[MyChat] 请按照提示输入：chat | logout.");
                    managerCommand.exec(scanner, channel);
                }
            }
        }).start();
    }

}
